package cn.net.xyan.easy.webapp.base.service.impl;

import cn.net.xyan.easy.webapp.base.service.IDTOService;
import cn.net.xyan.easy.webapp.base.service.IEntityResponseConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by zarra on 16/2/23.
 */
@Service
public class DefaultDTOService implements IDTOService{

    @Autowired
    List<IEntityResponseConvert> converts;

    Map<ClassPair,IEntityResponseConvert> convertMap = new HashMap<>();

    @Override
    public <R, E> R convert(Class<R> responseCls, E entity, Map<String, Object> context) {
        Class<E> entityCls = (Class<E>) entity.getClass();
        ClassPair classPair = new ClassPair(entityCls,responseCls);
        IEntityResponseConvert<R,E> convert = convertMap.get(classPair);
        return convert.convert(entity,context);
    }

    @Override
    public <R, E> R convert(Class<R> responseCls, E entity) {
        return convert(responseCls,entity,null);
    }

    @Override
    public <R, E> List<R> convert(Class<R> responseCls, Collection<E> entities) {
        List<R> result = new LinkedList<>();
        for (E entity:entities){
            if (entity!=null){
                result.add(convert(responseCls,entity));
            }
        }
        return result;
    }

    @Override
    public <R, E> Page<R> convert(Class<R> responseCls, Page<E> entities) {
        List<R> content = convert(responseCls,entities.getContent());

        long total = entities.getTotalElements();
        Pageable pageable = new PageRequest(entities.getNumber(), entities.getSize());
        Page<R> page =  new PageImpl<>(content, pageable, total);

        return page;
    }

    public static class ClassPair{
        Class entityCls;
        Class responseCls;

        public ClassPair(Class<?> entityCls,Class<?> responseCls){
            this.entityCls = entityCls;
            this.responseCls = responseCls;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            ClassPair classPair = (ClassPair) o;

            if (!entityCls.equals(classPair.entityCls)) return false;
            return responseCls.equals(classPair.responseCls);

        }

        @Override
        public int hashCode() {
            int result = entityCls.hashCode();
            result = 31 * result + responseCls.hashCode();
            return result;
        }
    }



    @PostConstruct
    public void setup(){
        for (IEntityResponseConvert c: converts){
            Method[] methods = c.getClass().getMethods();
            Method convertMethod = null;
            for (Method method : methods){
                if ("convert".equals(method.getName())){
                    convertMethod = method;
                }
            }

            if (convertMethod != null){
                Class<?> returnCls = convertMethod.getReturnType();
                Class<?> entityCls = convertMethod.getParameterTypes()[0];
                ClassPair pair = new ClassPair(entityCls,returnCls);
                convertMap.put(pair,c);
            }

        }
    }


}
